#!/usr/bin/env python

## Copyright (C) 2008 Navid Sheikhol-Eslami <navid@navid.it>

### This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import os, sys

def auto_update():

   if not os.path.isdir("%s/.svn" % CONFIG["rootdir"]):
      return False

   try:
      lastmod = time.localtime( os.stat("%s/.svn/entries" % CONFIG["rootdir"])[8] )

   except:
      return False

   if time.time() - time.mktime(lastmod) < 86400: # update every 24 hours
      return False

   from logst.helpers import command_runner

   upd = command_runner("svn status --non-interactive %s" % CONFIG["rootdir"], timeout = 5)

   for line in upd.run() [1].split("\n"):

      if line.split() [0] != "?" and line.split() [1] != "conf/simple_parser.local":
         print "cannot update, local repository has been modified. (svn diff)"
         return False

   upd = command_runner("svn update --quiet --non-interactive %s" % CONFIG["rootdir"], timeout = 30)
   upd.run()

   return True

if not sys.stdin.isatty():

   print "spawning new terminal"
   sys.argv[0] = os.path.abspath(sys.argv[0])
   os.system("gnome-terminal -e '%s'" % (' '.join(sys.argv)))
   sys.exit()

import time, glob, tempfile
import getopt, traceback, logging
import pdb

PROJECT_NAME = "logpox"

CONFIG = {
	"rootdir":sys.path[0],
	"confdir":"conf",
	"libdir":"lib",
	"sharedir":"share",
	"homedir":"%s/.%s" % (os.environ["HOME"], PROJECT_NAME),
	"autoupdate":True,
	"bindip": "0.0.0.0",
}

if os.path.exists(CONFIG["rootdir"] + "/" + CONFIG["libdir"]):
   for opt in CONFIG:
      if type(CONFIG[opt]) == str and CONFIG[opt][0] != "/": # not an abs path
         abspath = CONFIG["rootdir"] + "/" + CONFIG[opt]
         if os.path.exists(abspath):
            CONFIG[opt] = abspath

sys.path.append(CONFIG["libdir"])

try:    import cherrypy
except: print "python-cherrypy is missing. Exiting."; sys.exit(1)
from cherrypy.lib import cptools

# Auto-update

if CONFIG["autoupdate"]:

   auto_update()

# python < 2.4 (RHEL3 and RHEL4) doesn't have format_exc, activate work-around
if sys.version_info[0] < 2 or ( sys.version_info[0] == 2 and sys.version_info[1] < 4 ):
   def format_exc():
      import StringIO

      output = StringIO.StringIO()
      traceback.print_exc(file = output)
      toret = output.getvalue()
      output.close()
      return toret

   traceback.format_exc = format_exc

#os.environ['PYCHECKER'] = '-b cherrypy'
#import pychecker.checker

from logst.logs_abstraction import *

def usage():
   print "logpox [-p <port>] [messages.*, sysreport*, messages.gz etc etc]"

try:
   opts, args = getopt.getopt(sys.argv[1:], "hvp:", ["help", "port=","debug"])
except getopt.GetoptError:
   # print help information and exit:
   usage()
   sys.exit(2)

cmdline = {}
cmdline["verbose"] = 0
cmdline["firefox_spawn"] = False
cmdline["debug"] = False
cmdline["port"] = 8080

for o, a in opts:
   if o == "-v":
      cmdline["verbose"] += 1
   if o in ("-h", "--help"):
      usage()
      sys.exit()
   if o in ("-p", "--port"):
      cmdline["port"] = int(a)
   if o == "--debug":
      cmdline["debug"] = True

if __name__ != "__main__":
   sys.exit()

if not os.path.exists(CONFIG["homedir"]):
   os.mkdir(CONFIG["homedir"])
elif not os.path.isdir(CONFIG["homedir"]):
   logger.error("can't create homedir: %s" % CONFIG["homedir"])
   sys.exit(1)

CONFIG["port"] = 8080
CONFIG["tmpdir"] = tempfile.mkdtemp(prefix = ".logst_")

cluster_master = cluster_parser_class()
cluster_master.CONFIG = CONFIG

# initialize logging
logger = logging.getLogger('logpox')
logger.setLevel(logging.DEBUG)

logging.VERBOSE  = logging.INFO - 1
logging.VERBOSE2 = logging.INFO - 2
logging.VERBOSE3 = logging.INFO - 3 
logging.addLevelName(logging.VERBOSE, "verbose")
logging.addLevelName(logging.VERBOSE2,"verbose2")
logging.addLevelName(logging.VERBOSE3,"verbose3")

CONFIG["log_file"] = "%s/logpox.log" % CONFIG["tmpdir"]
# log to a file
tmplog = logging.FileHandler(CONFIG["log_file"])
tmplog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
tmplog.setLevel(logging.DEBUG)
logger.addHandler(tmplog)

# define a Handler which writes INFO messages or higher to the sys.stderr
tmplog = logging.StreamHandler(sys.stderr)
if cmdline["debug"]:
   tmplog.setLevel(logging.DEBUG)
elif cmdline["verbose"] > 0:
   tmplog.setLevel(20 - cmdline["verbose"])
else:
   tmplog.setLevel(logging.INFO)
tmplog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
logger.addHandler(tmplog)

# let's add the log files

logger.info("adding logs")

for logname in remove_duplicates_from_list(args):
   logger.info(" - %s" % logname)
   cluster_master.add_log(logname)

logger.info("finished adding logs")

if len(cluster_master.hosts) == 0:
   logger.error("no valid data to parse was found. exiting.")
   sys.exit(1)

for fparser in glob.glob("%s/logst/parsers/*_parser.py" % CONFIG["libdir"]):
   fparser = os.path.basename(fparser)
   plugin_name = fparser[:-10]
   try:
      plugin_class = __import__("logst.parsers." + fparser[:-3], globals(), locals(), [fparser[:-10]])
      plugin = getattr(plugin_class, plugin_name + "_Parser")
      cluster_master.register_parser(plugin(cluster_master, CONFIG))
   except:
      logger.error("error importing parser: %s" % plugin_name)
      logger.debug( traceback.format_exc() )
   else:
      logger.info("imported parser: %s" % plugin_name)

cluster_master.events_populator = events_populator_class(cluster_master)

from logst.page_handlers.root import webui_root

cherrypy.root = webui_root()
cherrypy.root.CONFIG = CONFIG
cherrypy.root.cluster_master = cluster_master

for phandler in glob.glob("%s/logst/page_handlers/*.py" % CONFIG["libdir"]):
   phandler = os.path.basename(phandler)[:-3]

   if phandler in [ "__init__", "root" ]:
      continue

   logger.info("loading page handler: %s" % phandler)
   try:
      ph_class = __import__("logst.page_handlers." + phandler, globals(), locals(), "%s_class" % phandler)
      ph = getattr(ph_class, phandler)()
      ph.cluster_master = cluster_master
      setattr(cherrypy.root, phandler, ph)
   except:
      logger.error("error importing page handler: %s\n" % phandler)
      logger.error( traceback.format_exc() )
      sys.exit()

cluster_master.parse_hyper(threaded = True)
cluster_master.slow_internal_parser(threaded = True)

if cmdline["debug"]:
   pdb.set_trace()

cherrypy.config.update({'session_filter.on': True})
cherrypy.config.update({'autoreload.on': False})
cherrypy.config.update({'server.environment': "production"})

cherrypy.config.update({'profiling.on': False})
cherrypy.config.update({'profiling.path': "/tmp/profiling"})
# python /usr/lib/python2.4/site-packages/cherrypy/lib/profiler.py /tmp/profiling/ 8001

cherrypy.config.update({'server.socket_port': cmdline["port"], 'server.socket_host': CONFIG["bindip"] })

cherrypy.config.update({
	"/":{'static_filter.root': os.path.abspath(CONFIG["sharedir"])},
	"/overlib":{'static_filter.on': True, "static_filter.dir":"html/overlib"},
	"/static":{'static_filter.on': True, "static_filter.dir":"html"}
})

if cmdline["firefox_spawn"]:
   os.system("firefox http://localhost:%s &" % CONFIG["port"])

__breakHits__ = 0

def clean_up(signum, frame):

   cherrypy.server.stop()

   from threading import enumerate
   global __breakHits__

   logger = logging.getLogger('logpox')

   __breakHits__ += 1

   logger.info("shutdown requested (%d break hits)" % __breakHits__)

   if __breakHits__ == 2:	# 2 CTRL+c do nothing.
      return
   if __breakHits__ >= 3:	# 3 CTRL+c triggers suicide.
      os.kill(os.getpid(), signal.SIGKILL)
      sys.exit(-1)

   logger.info("waiting for threads to finish")

   for thread in enumerate():

      if thread.getName() in [ "MainThread", "expireCache" ]:
         continue

      # until we find a way to kill threads in case of > 1 CTRL+C, ignore KeyboardInterrupt
      while thread.isAlive():
         logger.debug("waiting for thread %s" % thread.getName())
         try:
            thread.join()
         except KeyboardInterrupt:
            clean_up()

   logger.info("all threads ended, cleaning up.")

   if CONFIG["tmpdir"].startswith("/tmp"):
      os.system("/bin/rm -rf %s" % CONFIG["tmpdir"])

   logger.info("finished.")

cherrypy.server.on_stop_server_list.insert(0, cherrypy.root.shutdown)
cherrypy.server.on_stop_server_list.insert(0, cluster_master.shutdown)
#cherrypy.server.on_stop_server_list.append(clean_up)

signal.signal(signal.SIGTERM, clean_up)

cherrypy.server.start()
